﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.Xml;
using Catsland.Core;
using Catsland.Editor;
using System.ComponentModel;

namespace Catsland.CatsPlugin
{
	public class Animator : CatComponent
	{
		int m_currentIndex;
		int m_timeLastFrame;
		bool m_isPlaying;
		bool m_isPong;
		int m_millionSecondPreFrame;

        public string m_currentAnimationName;

         [CategoryAttribute("Animation"),
            EditorAttribute(typeof(PropertyGridAnimationSelector),
            typeof(System.Drawing.Design.UITypeEditor))]
        public string AnimationName
        {
            get { return m_currentAnimationName; }
            set { PlayAnimation(value); }

        }
        private bool m_isMirror = false;
         [CategoryAttribute("Animation")]
        public bool Mirror
        {
            get { return m_isMirror; }
            set { m_isMirror = value; }
        }

		public Animator(GameObject gameObject)
			: base(gameObject)
		{
		}

		public override CatComponent CloneComponent(GameObject gameObject)
		{
			Animator animator = new Animator(gameObject);
			return animator;
		}

		// needs: quadRender, quadRender.Animation, Model,

		public override void Initialize(Scene scene)
		{
			base.Initialize(scene);

			// configuration
            ModelComponent modelComponent = (ModelComponent)m_gameObject.GetComponent(typeof(ModelComponent).Name);
            if (modelComponent == null)
            {
                System.Console.Out.WriteLine("No ModelComponent needed by Animator.");
                return;
            }
			Animation animation = modelComponent.Model._animation;

			m_timeLastFrame = 0;
			m_isPlaying = animation._isAutoPlay;
			m_currentIndex = 0;
			m_currentAnimationName = animation._defaultAnimationClipName;
			m_isPong = false;
			m_millionSecondPreFrame = animation._millionSecondPerFrame;
		}

        public override void EditorUpdate(int timeLastFrame)
        {
            base.EditorUpdate(timeLastFrame);

            Update(timeLastFrame);
        }

		public override void Update(int timeLastFrame)
		{
			base.Update(timeLastFrame);

            ModelComponent modelComponent = (ModelComponent)m_gameObject.GetComponent(typeof(ModelComponent).Name);
            if (modelComponent == null)
            {
                Console.Out.WriteLine("No ModelComponent for Animator.");
                return;
            }

			Animation animation = modelComponent.Model._animation;
            QuadRender quadRender = (QuadRender)m_gameObject.GetComponent(typeof(QuadRender).Name);

			float width_per_frame = 1.0f / animation._tiltUV.X;
			float height_per_frame = 1.0f / animation._tiltUV.Y;

			if (m_isPlaying)
			{
				m_timeLastFrame += timeLastFrame;
			}

			if (m_timeLastFrame >= m_millionSecondPreFrame)
			{
				m_timeLastFrame -= m_millionSecondPreFrame;
				AnimationClip current_clip = animation.getAnimationClip(m_currentAnimationName);
				if (current_clip == null)
				{
					Console.WriteLine("Error! Can not find animation clip: " + m_currentAnimationName);
					return;
				}
				if (current_clip._mode == AnimationClip.PlayMode.PINGPONG)
				{
					if (!m_isPong) // ping
					{
						if (m_currentIndex < current_clip._endIndex)
						{
							++m_currentIndex;
						}
						else
						{
							--m_currentIndex;
							m_isPong = true;
						}
					}
					else
					{ // pong
						if (m_currentIndex > current_clip._beginIndex)
						{
							--m_currentIndex;
						}
						else
						{
							++m_currentIndex;
							m_isPong = false;
						}
					}
				}
				else
				{
					if (m_currentIndex < current_clip._endIndex)
					{
						++m_currentIndex;
					}
					else
					{
						if (current_clip._mode == AnimationClip.PlayMode.CLAMP)
						{
							m_isPlaying = false;
						}
						else if (current_clip._mode == AnimationClip.PlayMode.LOOP)
						{
							m_currentIndex = current_clip._beginIndex;
						}
						else if (current_clip._mode == AnimationClip.PlayMode.STOP)
						{
							m_currentIndex = current_clip._beginIndex;
							m_isPlaying = false;
						}
					}
				}
			}

			Point current_nxm = new Point();
			current_nxm.X = m_currentIndex % animation._tiltUV.X;
			current_nxm.Y = m_currentIndex / animation._tiltUV.X;

			quadRender.m_vertex[0].TextureCoordinate.X = (current_nxm.X + (m_isMirror ? 1 : 0)) * width_per_frame;
			quadRender.m_vertex[0].TextureCoordinate.Y = (current_nxm.Y + 1) * height_per_frame;
			quadRender.m_vertex[1].TextureCoordinate.X = (current_nxm.X + (m_isMirror ? 1 : 0)) * width_per_frame;
			quadRender.m_vertex[1].TextureCoordinate.Y = current_nxm.Y * height_per_frame;
			quadRender.m_vertex[2].TextureCoordinate.X = (current_nxm.X + (m_isMirror ? 0 : 1)) * width_per_frame;
			quadRender.m_vertex[2].TextureCoordinate.Y = (current_nxm.Y + 1) * height_per_frame;
			quadRender.m_vertex[3].TextureCoordinate.X = (current_nxm.X + (m_isMirror ? 0 : 1)) * width_per_frame;
			quadRender.m_vertex[3].TextureCoordinate.Y = current_nxm.Y * height_per_frame;
		}

		public void fadeToAnimation(String animationClipName)
		{
			// cur_index
            ModelComponent modelComponent = (ModelComponent)m_gameObject.GetComponent(typeof(ModelComponent).Name);
            if (modelComponent == null)
            {
                Console.Out.WriteLine("No ModelComponent for Animator.");
                return;
            }
			Animation animation = modelComponent.Model._animation;
			AnimationClip cur_clip = animation.getAnimationClip(m_currentAnimationName);
			int cur_beginIndex = cur_clip._beginIndex;
			int cur_endIndex = cur_clip._endIndex;
			// target index
			AnimationClip target_clip = animation.getAnimationClip(animationClipName);
			int target_beginIndex = target_clip._beginIndex;
			int target_endIndex = target_clip._endIndex;

			int index = target_beginIndex 
				+ (int)((target_endIndex - target_beginIndex + 1) * (float)(m_currentIndex - cur_beginIndex) / (cur_endIndex - cur_beginIndex + 1));

			m_currentIndex = index;
			m_currentAnimationName = animationClipName;
			m_isPlaying = true;
		}

		public void PlayAnimation(String animationClipName)
		{
            ModelComponent modelComponent = (ModelComponent)m_gameObject.GetComponent(typeof(ModelComponent).Name);
            if (modelComponent == null)
            {
                Console.Out.WriteLine("No ModelComponent for Animator.");
                return;
            }
			Animation animation = modelComponent.Model._animation;
			AnimationClip target_clip = animation.getAnimationClip(animationClipName);
			if (target_clip == null)
			{
				return;
			}
			m_currentIndex= target_clip._beginIndex;
			m_currentAnimationName = animationClipName;
			m_isPlaying = true;
		}

		public void CheckPlayAnimation(String animationClipName)
		{
			if (m_currentAnimationName != animationClipName)
			{
				PlayAnimation(animationClipName);
			}
		}

		public void Pause()
		{
			m_isPlaying = false;
		}

		public override bool SaveToNode(XmlNode node, XmlDocument doc)
		{
			XmlElement animator = doc.CreateElement(typeof(Animator).Name);
			node.AppendChild(animator);

			animator.SetAttribute("currentClip", m_currentAnimationName);
			animator.SetAttribute("isXMirror", "" + m_isMirror);

			return true;
		}

        public override void ConfigureFromNode(XmlElement node, Scene scene, GameObject gameObject)
        {
            base.ConfigureFromNode(node, scene, gameObject);

            m_currentAnimationName = node.GetAttribute("currentClip");
            m_isMirror = bool.Parse(node.GetAttribute("isXMirror"));
        }

        public override CatsModel GetModel()
        {
            ModelComponent modelComponent = (ModelComponent)m_gameObject.GetComponent(typeof(ModelComponent).Name);
            return modelComponent.Model;
        }
	}
}
